package com.light.gulimall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.light.common.pojo.ESPojo;
import com.light.common.utils.PageUtils;
import com.light.common.utils.Query;
import com.light.gulimall.product.constants.RedisConstants;
import com.light.gulimall.product.dao.CategoryDao;
import com.light.gulimall.product.entity.CategoryEntity;
import com.light.gulimall.product.service.CategoryService;
import com.light.gulimall.product.vo.CategoryJsonVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 组装树形结构
     *
     * @return 查询结果
     */
    @Override
    public List<CategoryEntity> listWithTree() {
        //查询全量数据
        List<CategoryEntity> list = list();
        //组装树形结构
        return this.getClassifyTree(list);
    }

    /**
     * 根据ID 逻辑删除商品分类
     *
     * @param ids
     */
    @Override
    public void removeCategoryByIds(List<Long> ids) {
        removeByIds(ids);
    }

    @Override
    public void getCateInfoById(Long catalogId, ESPojo esPojo) {
        CategoryEntity category = getById(catalogId);
        esPojo.setCatalogName(category.getName());
    }

    @Override
    @Cacheable(value = {"category"},key = "'getLevelOneCateGory'") //放入缓存 如果缓存中有方法就不调用
    public List<CategoryEntity> getLevelOneCateGory() {
        return list(new QueryWrapper<CategoryEntity>().eq("parent_cid", "0"));
    }

    @Override
    public Map<String, List<CategoryJsonVO>> getCategoryJson() {
        //从缓存中获取
        String category = stringRedisTemplate.opsForValue().get(RedisConstants.CATEGORY_KEY);
        //缓存中不为空
        if (StringUtils.isNotBlank(category)) {
            log.info("查询到了结果");
            return JSON.parseObject(category, new TypeReference<Map<String, List<CategoryJsonVO>>>() {
            });
        }

        /*
        缓存空值解决缓存穿透
        设置过期时间（随机值）解决缓存雪崩
        加锁解决缓存击穿
         */
        //查询pms_category表的全量数据
        Map<String, List<CategoryJsonVO>> map;
        map = this.getCateGoryFromDB();
        return map;
    }

    /**
     * 从数据库查询三级分类
     * 分布式锁解决缓存击穿
     * @return 查询结果
     */
    private Map<String, List<CategoryJsonVO>> getCateGoryFromDB() {
//        synchronized (this) {
        RLock lock = this.redissonClient.getLock(RedisConstants.CATEGORY_LOCK_KEY);
        lock.lock(10, TimeUnit.SECONDS);
        try {
            log.info("获取到了锁");
            //再看下缓存中有没有
            //从缓存中获取
            String category = stringRedisTemplate.opsForValue().get(RedisConstants.CATEGORY_KEY);
            //缓存中不为空
            if (StringUtils.isNotBlank(category)) {
                log.info("查询到了结果");
                return JSON.parseObject(category, new TypeReference<Map<String, List<CategoryJsonVO>>>() {
                });
            }
            log.info("开始查询数据库");
            List<CategoryEntity> list = list();
            Map<String, List<CategoryJsonVO>> map = list.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
                //查出某个一级分类下的所有二级分类
                //            List<CategoryEntity> entityList = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));
                List<CategoryEntity> entityList = list.stream().filter(categoryEntity -> categoryEntity.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
                List<CategoryJsonVO> categoryJsonVOS = entityList.stream().map(categoryEntity -> {
                    CategoryJsonVO jsonVO = new CategoryJsonVO();
                    jsonVO.setCatalog1Id(String.valueOf(categoryEntity.getParentCid()));
                    jsonVO.setId(String.valueOf(categoryEntity.getCatId()));
                    jsonVO.setName(categoryEntity.getName());
                    //查出某个二级分类下的所有三级分类
                    //                List<CategoryEntity> entityListThree = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", categoryEntity.getCatId()));
                    List<CategoryEntity> entityListThree = list.stream().filter(categoryEntity1 -> categoryEntity1.getParentCid().equals(categoryEntity.getCatId())).collect(Collectors.toList());
                    List<CategoryJsonVO.CatalogJsonThree> catalogJsonThrees = entityListThree.stream().map(categoryEntity1 -> {
                        CategoryJsonVO.CatalogJsonThree catalogJsonThree = new CategoryJsonVO.CatalogJsonThree();
                        catalogJsonThree.setId(String.valueOf(categoryEntity1.getCatId()));
                        catalogJsonThree.setName(categoryEntity1.getName());
                        catalogJsonThree.setCatalog2Id(String.valueOf(categoryEntity1.getParentCid()));
                        return catalogJsonThree;
                    }).collect(Collectors.toList());
                    jsonVO.setCatalog3List(catalogJsonThrees);
                    return jsonVO;
                }).collect(Collectors.toList());

                return categoryJsonVOS;
            }));
            //向缓存中存一份(序列化)
            stringRedisTemplate.opsForValue().set(RedisConstants.CATEGORY_KEY, CollectionUtils.isEmpty(map) ? "0" : JSON.toJSONString(map), 1, TimeUnit.DAYS);
            return map;
        } finally {
            lock.unlock();
        }
//        }
    }

    public List<CategoryEntity> getClassifyTree(List<CategoryEntity> childClassifyResp) {
        // 返回的树形数据
        List<CategoryEntity> tree = new ArrayList<CategoryEntity>();
        // 第一次遍历
        for (CategoryEntity treeClassify : childClassifyResp) {
            // 找到根节点，这里我的根节点的pid为0
            if (treeClassify.getParentCid() == 0) {
                tree.add(this.findChild(treeClassify, childClassifyResp));
            }
        }
        // 使用stream流根据sort字段进行排序
        return tree.stream()
                .sorted(Comparator.comparingInt(CategoryEntity::getSort))
                .collect(Collectors.toList());
    }

    private CategoryEntity findChild(CategoryEntity treeClassify, List<CategoryEntity> list) {
        // 定义list用于存储子节点
        List<CategoryEntity> children = new ArrayList<CategoryEntity>();
        for (CategoryEntity node : list) {
            // 找到根节点，这里我的根节点的pid为0
            if (node.getParentCid().equals(treeClassify.getCatId())) {
                // 调用递归
                children.add(findChild(node, list));
            }
        }
        treeClassify.setSubList(children);
        return treeClassify;
    }


}